Skip to content

[pull] master from JuliaLang:master - #4

Open
pull[bot] wants to merge 394 commits into
Julia-Embedded:masterfrom
JuliaLang:master
Open

pull[bot] wants to merge 394 commits into
Julia-Embedded:masterfrom
JuliaLang:master

Conversation

@pull

@pull pull Bot commented Nov 24, 2024 •

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.1)

Can you help keep this open source service alive? 💖 Please sponsor : )

@pull pull Bot added the ⤵️ pull label Nov 24, 2024
IanButterworth and others added 29 commits July 25, 2025 15:55
…ion (#3848)

Co-authored-by: KristofferC <kristoffer.carlsson@juliacomputing.com>
IanButterworth and others added 30 commits September 24, 2026 18:54
Since JuliaLang/julia#59765 (Julia 1.13) `Base.Precompilation.precompilepkgs`
returns the cache files it found or produced, which code loading uses.
`Pkg.precompile` passed that value through, so calling it at the REPL
started printing a vector of `.ji` paths. That value is not part of
`Pkg.precompile`'s API; return `nothing` as before.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Precompile cache file names are keyed on the project path, so two
environments mounted at the same path (containers sharing a depot, both
with their project at /work) get the same cache file names and overwrite
each other's caches (JuliaLang/julia#63268). The manifest now carries an
identity Julia can key the names on instead.

`environment_id` is the project uuid when the project has one, otherwise
a UUID generated the first time the manifest is written and kept from
then on. `environment_name` mirrors the project name and is left out when
there is none. Both are refreshed by `write_env` whenever an existing or
changed manifest is written, so they follow later edits to the project
file; a manifest is never created just for them. In a workspace they come
from the root project, whose directory holds the manifest.

A generated id is marked with a trailing `# generated` comment, since
nothing else in the file says where it came from.

Writing the entries bumps the manifest format to 2.2.

Co-authored-by: Claude Code (Fable 5.1) <noreply@anthropic.com>
… Pkg.test (#4830)

gen_target_project copied the tested package's [sources] into the sandbox
project unchanged, and sandbox then resolved relative paths against the
active project. Testing a workspace member from the workspace root therefore
looked for its path sources relative to the root.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Detect URLs in `REPLMode` with the shared `isurl`

`src/REPLMode/argument_parsers.jl` carried its own URL detection in `looks_like_url`, and a second, stricter list in `looks_like_complete_url`. The two had drifted, so `extract_url_revision` left a `#rev` suffix inside the URL for any SSH user other than `git`:

    pkg> add myorg@host:v3/org/proj/Repo.jl#main

Both now go through `isurl`, which that file already imports, and `looks_like_complete_url` defers to `looks_like_url`, so the two cannot diverge again.

The guard that was inlined in `looks_like_url` becomes `looks_like_version_spec`: an all-numeric host in `user@host:path` is a version, not an address, unless it has the three dots of an IPv4 address.

A string containing `.git` is no longer a URL on that basis alone. The regex replaced in 94d9551 required `.git` at the end of an otherwise complete URL, so the suffix narrowed the match; carried over as a standalone `contains` test it widened it instead, and matched `.gitignore` and `foo.github.io`. `isurl` treats the suffix as optional, which is what it was.

`looks_like_url` gains `file://`, which `isurl` recognises.

Fixes #4826

* Preserve explicit URL scheme detection

---------

Co-authored-by: Kristoffer Carlsson <kcarlsson89@gmail.com>
`is_local_repo` called `ispath` on the URL. That is a `stat` syscall against a PATH_MAX-bounded buffer, so a URL longer than PATH_MAX raised ENAMETOOLONG instead of returning `false`, aborting the enclosing clone or fetch before any network activity. PATH_MAX is 1024 on macOS and 4096 on Linux, so a remote may work on Linux and fail on macOS.

`ispath_nothrow` is added alongside the `isdir_nothrow` and `isfile_nothrow` already in `src/utils.jl`, and like them answers `false` rather than propagating. `is_local_repo` now returns where it raised, and is otherwise unchanged.

Fixes #4825
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Since #3848 an `add` auto-precompiles only the added packages and their
dependencies. When the resolve also changes packages that were already in
the manifest, such as downgrading a dependency to satisfy the new
requirement, the packages that depend on them keep caches built against
the old versions, and are only rebuilt the next time they are loaded.

Now the changed packages and everything that depends on them are
precompiled along with the added packages. The rest of the environment is
still left alone, as #3848 intended.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
`contrib/list_missing_pkg_tags.jl` cloned the whole julia repository and
checked out every release tag to find which releases bundle a Pkg commit
that has no matching tag here. The new manually triggered workflow reads
julia's tag list with `git ls-remote` and fetches `stdlib/Pkg.version` only
for releases that lack a Pkg tag. It checks that each commit exists in this
repository and writes the `git tag` and `git push` commands to the job
summary.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
`extract_url_revision` asked `looks_like_complete_url`, which wanted a `.` or a `/` on top of being a URL, so a scp-like URL with a dotless host and a slashless path kept its suffix:

    pkg> add git@server:repo#main

That test helped when `looks_like_url` was a list of prefixes. It now goes through `isurl`, which already requires a colon and a path, so `looks_like_complete_url` is removed and `extract_url_revision` calls `looks_like_url`.
…hallow registry test (#4843)

* CI: run the Windows tests under the default git line-ending config

The Windows job set `core.autocrlf false` globally so that the checkout
kept the test fixtures' LF line endings. That also changed how LibGit2
checks out the repositories the tests create, so tests passed here that
fail on machines with Git for Windows' default `core.autocrlf true`,
such as julia's Buildkite Windows agents.

Keep the fixtures byte-for-byte with a `.gitattributes` instead, and drop
the global override.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* test: compare the shallow registry update result independent of line endings

The checkout follows `core.autocrlf`, which Git for Windows turns on by
default, so the file can have CRLF line endings there.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ves (#4844)

The precompile workload registered TestPkg under the tree hash of its git
commit, but served a tarball made from the files on disk. On Windows the
two hashes differ, so the download failed its hash check with a warning
and Pkg fell back to cloning the repository, leaving the pkg server
download path uncovered there.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* test: load test packages in a subprocess

Test files share worker processes, so a package that one test file loads
stays loaded for the later files on that worker, where it changes the
answers of for example `Base.isprecompiled` and package resolution. Load
test packages in a fresh julia process instead. The read-only checks
of an installed package look up its source file without loading it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: check that no test file loads packages into the test process

After each test file, fail if it left a package loaded other than a
stdlib, a package of the test environment, or an extension of those, and
name the packages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…#4845)

The server behind the download cancellation test only signalled a
disconnect when the client closed cleanly. A client that closes with
unread data in its receive buffer resets the connection instead, `eof`
then throws `ECONNRESET`, and the signal was skipped. The cancelled curl
transfer often ends that way on FreeBSD, where the test timed out in
every recent Pkg bump on JuliaLang/julia CI.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Use Resolver.jl's SAT-based resolver by default

Switch the default resolver to Resolver.jl (exact SAT-based resolution with
an explicit preference order and a diagnosis on unsatisfiable resolves); the
legacy maxsum resolver stays selectable with `JULIA_PKG_RESOLVER=maxsum`.
Resolver.jl comes in through `[sources]`, pinned at its current `main`.

`src/SATResolve.jl` adapts the output of `Operations.deps_graph` to
Resolver.jl's `PkgData` universe and `Problem`:

- Only the strong-dependency closure of the requirements is built. Packages
  reachable through weak edges alone can never be installed, and building
  conflict matrices for the whole weak closure was the dominant cost.
- Requirement version specs and project `[compat]` entries are passed as
  `compat`, pins as `pin`, and the active/workspace projects are folded into
  those constraints instead of being modelled as packages, so a diagnosis
  can say "your compat leaves ...", "your pin leaves ...", "unpin X".
- A `Diagnosis` is rebuilt over package names and rendered with Resolver's
  own report; fixes that would drop the synthetic julia requirement are
  filtered out. A requirement matching no available version is reported
  directly with the versions that exist.
- The diagnosis only runs on the final tier of `tiered_resolve`
  (`diagnose_unsat`).

Tests: a `resolver backends` testset in `test/api.jl`, backend-agnostic
wording in two existing assertions, and `Utils.host_deps_depot` appended to
the depot of subprocess tests that load the dev Pkg in an isolated depot.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Update Resolver.jl pin to current main

Picks up the report wording from the review (restricts/constrains ... to,
dependency instead of requirement, a note instead of "Blocked fixes") and
the shadow-aware ranges, so the rendering nits are gone. `Conflict` gained a
`shadows` field, passed through when the diagnosis is renamed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* CI: instantiate the docs workspace with the shipped Pkg

The docs step loaded the checkout's Pkg with `--project` before any
Manifest existed, which now fails since Resolver is not a stdlib. Use the
shipped Pkg for the instantiate; it writes the workspace Manifest with
Resolver, and the docs build then loads the checkout's Pkg as before.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Copy the artifacts of Pkg's deps into the loaded test depot

The `concurrent` tests spawn Julia processes that load the dev Pkg from an
isolated depot path (a temp depot plus the shared loaded depot). With the
SAT resolver, Pkg depends on `Resolver` and thereby `libpicosat_jll`;
`copy_this_pkg_cache` carried their source and compile caches into the
loaded depot but not their artifacts, so the JLL failed in `__init__`
with `Artifact "libpicosat" was not found` and all CI test jobs failed.

Copy the host-platform artifacts of each dependency checkout along with
its source, selected from the package's `Artifacts.toml`. Appending the
host depot with `host_deps_depot()` instead would leak the host's
packages into the test, which asserts that exactly one process installs
FFMPEG.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Keep deps whose compat excludes a non-upgradable stdlib in the SAT resolver

`SATResolve.build_pkg_data` dropped the dependency edge to a non-upgradable
stdlib when the registry compat on it excluded the stdlib version shipped
with this julia, so the stdlib never made it into the package's manifest
deps. Relax the compat to `VersionSpec()` and keep the edge instead, as
`Resolve.Graph` does since #4802.

Fixes the `stdlib_compat` test for #4801 on this branch.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Update Resolver.jl pin to current main

Picks up sourced constraint kinds and the `sources` field on `Diagnosis`,
which the adapter does not use yet, and the polynomial `lift_actions`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Add a changelog entry for the SAT-based resolver

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Give every isolated test depot the caches of Pkg's loaded dependencies

`Base.compilecache` tells a precompile worker to keep the modules its
parent has loaded, at the parent's build ids. The test process now has
Preferences, JLLWrappers, libpicosat_jll and Resolver loaded through Pkg,
so a test that precompiled anything depending on Preferences or
JLLWrappers in a fresh depot (`BigProject` in test/repl.jl) failed with
"Module Preferences with build ID … is missing from the cache" and then a
strict-mode error, since the fresh depot held no cache for those build
ids. The copies that `copy_this_pkg_cache` made into the loaded depot only
covered `isolate(loaded_depot = true)`.

Populate a `DEPS_DEPOT` once per test run with the loaded dependencies'
compile caches, sources and artifacts, and append it to the depot path of
every `isolate` and `temp_pkg_dir` depot. Subprocesses that get
`DEPOT_PATH` now find Pkg's dependencies through it, which replaces
`host_deps_depot()`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Skip Aqua's persistent-tasks check while Resolver is a registry dependency

The check loads Pkg from a wrapper environment whose manifest Aqua
synthesizes without the `[deps.X.syntax]` tables Pkg records. Resolver's
cache, recorded with the syntax version of this julia since it declares no
julia compat, is rejected there as "parsed for a different Julia syntax
version", its dependencies are recompiled in the host depot and the cache
of Pkg is orphaned, so the check ends in "Precompiling Pkg is disallowed".
Skip it until Aqua carries the syntax entries over, or Resolver is bundled
with Pkg and has no manifest entry.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Vendor Resolver.jl

Pkg is a stdlib and cannot depend on a registered package, so carry an
unmodified copy of Resolver.jl in vendor/Resolver. vendor/update_resolver.sh
replaces it with a given upstream revision (or one from a local checkout) and
records the commit in vendor/Resolver.version; with --check, run in CI, it
verifies the copy matches that commit. The copy keeps upstream's style, so
Runic, pre-commit and codecov skip vendor/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Load the vendored Resolver instead of the registry package

Resolver becomes `Pkg.Resolver`, and Pkg depends on its dependencies, the
libpicosat_jll and Serialization stdlibs. Pkg thereby has no non-stdlib
dependencies again, so drop the workarounds that needed: the deps depot of
the tests, the copied artifacts, the skipped Aqua persistent-tasks check and
the docs instantiate with the shipped Pkg. The closure-box and unbound-args
checks skip the vendored code, which is checked upstream.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep the legacy resolver out of Pkg's image

The precompile workload resolves with the SAT resolver, but inference also
followed the maxsum branch, which is chosen at runtime, and compiled it into
Pkg's image. Call it through an inference barrier: it is opt-in, and its
first use may take the compile latency.

julia master, median of 9 `import Pkg`, Pkg master alongside:

                                 before    after
    import Pkg (Pkg master)      907 ms    842 ms   (522 / 537 ms)
    Pkg's package image          62.8 MB   57.7 MB
    cached specializations
      of or on Pkg code          22607     20568
      of or on Pkg.Resolve code  2102      128

The first resolve with JULIA_PKG_RESOLVER=maxsum now compiles for ~1.4 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Run the SAT resolver on package names

The resolver ran on UUIDs, and its diagnosis was rebuilt over display names
for the report, so all of the report code, and the data it prints, was
compiled for both. Key the resolver's input by the display names (unique per
resolve) from the start and map the solution back to UUIDs; the priority
order is still by name, then UUID, and the report reads as before.

julia master, median of 9 `import Pkg`, Pkg master alongside:

                                 before    after
    import Pkg (Pkg master)      842 ms    836 ms   (537 / 548 ms)
    Pkg's package image          57.7 MB   56.1 MB
    cached specializations
      of or on Pkg code          20568     19678
      of or on Resolver code     9896      8948

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Always give the SAT resolver a version order

Without preferred versions the order was `nothing`, so the resolve call took
`Union{Nothing, <closure>}`; inference widened that call to `Function`
arguments and compiled the resolver for those as well. Give the order in
every case: newest first is the universe's own layout, which the resolver
detects in one pass.

julia master, median of 9 `import Pkg`, Pkg master alongside:

                                 before    after
    import Pkg (Pkg master)      836 ms    816 ms   (548 / 537 ms)
    Pkg's package image          56.1 MB   55.6 MB
    cached specializations
      of or on Pkg code          19678     19529
      of Resolver's own methods  1470      1328

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Update vendored Resolver.jl to 159d919

Changes since dc5186b:
159d919 Do not resolve recursively in upstream probes
7e4dd9a Do not parameterize DepsProvider on its provider
b59ce58 Make Problem concrete in its constraint and source maps
5654339 Give Constraint an inner constructor
e8202c9 Remove closure boxes

(kc/ttfx on top of JuliaLang/Resolver.jl#131, not upstream yet.)

julia master, median of 9 `import Pkg`, Pkg master alongside:

                                 before    after
    import Pkg (Pkg master)      816 ms    674 ms   (537 / 558 ms)
    Pkg's package image          55.6 MB   50.2 MB
    cached specializations
      of or on Pkg code          19529     16782
      of or on Resolver code     8806      6077
      of Resolver's own methods  1328      821

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Update vendored Resolver.jl to 3faf4bc

Same content as 159d919, now that JuliaLang/Resolver.jl#131 and #132
are merged on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Check vendored Resolver for closure boxes and unbound args again

Fixed upstream in JuliaLang/Resolver.jl#131, which is now vendored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* Share manifest parsing across constructor and input specializations

* Share status rendering, registry updates, and Project parsing

* Share GC usage-path collection without nested generators
* Name the workspace's project files when their compat entries collide

In a workspace every member's compat entry on a package is intersected into
one spec before anything is resolved, and when that intersection is empty the
error said only that it was: "empty intersection between JSON@* and project
compatibility ∅". Which member wrote which entry, and so which file to open,
the reader had to work out by hand.

The error now lists the entries it intersected, each with the project file it
came from, relative to the workspace root. Outside a workspace nothing
changes: there is one project file, and it is the one the reader is in.

Co-Authored-By: Kristoffer Carlsson <kcarlsson89@gmail.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Name the workspace project file and line in resolver errors

In a workspace the compat entries of every project file are intersected into
one requirement spec, so a failed resolve could only say "your compat
restricts X" and "drop dependency X", leaving the reader to find which member
declares it -- and, in a workspace of any size, to grep for it.

Each member's compat entry now goes to the SAT resolver as a constraint of its
own, under a kind that names where it was declared, and each requirement
records the files that list it as a dependency. The diagnosis then prints the
place after the thing: "your compat (Sub1/Project.toml:5) restricts DataFrames
to >=1.7.0", "relax your compat on PrettyTables (Sub2/Project.toml:5)", "drop
dependency DataFrames (Sub1/Project.toml:2)". The place is a `path:line` so
that an editor opens at the entry to change; TOML.parse drops line numbers, so
the line is found by scanning the file for the entry, and a file whose entry
cannot be found is named by its path alone.

A file's entry is attributed only where the requirement spec honours it -- a
stdlib's spec is let float regardless of what the members say -- and an entry
that admits every version of the package is no constraint and is not named.
Outside a workspace nothing changes.

Co-Authored-By: Kristoffer Carlsson <kcarlsson89@gmail.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Use the platform's path separator in workspace resolver-error tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Kristoffer Carlsson <kcarlsson89@gmail.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Changes since 3faf4bc (JuliaLang/Resolver.jl#133):
- e021073 Avoid nested iterator machinery in diagnosis printing
- 8843a8f Avoid nested generator inference in diagnostic cover collection
- d0d2570 Share reachability and compaction kernels across optional inputs
- c9ed325 Share `mark_necessary!` pruning kernel across setup specializations

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…4850)

When a package is registered in several registries, two code paths applied a
registry's `Deps.toml`/`Compat.toml` ranges to versions that registry does not
have (#4849). The resolver already guards against this through
`query_compat_for_version_multi_registry!`, but:

- `Registry.query_deps_for_version` unioned deps from every registry whose
  range covered the version when building the manifest, so a dependency only
  declared by a registry lacking the resolved version could end up in the
  manifest entry.
- `Operations.status_compat_info` queried every registry for a dependent's
  compat, so `Pkg.status(outdated = true)` could report that dependent as
  holding a package back based on a registry that does not contain the
  dependent's version.

`query_deps_for_version` now takes the per-registry version sets that
`deps_graph` already computes and skips registries without the version, and
`status_compat_info` only consults registries whose `version_info` has the
dependent's version.

Fixes #4849

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…4818)

* Remove [sources] entry for PTR

* [temp] --verbose

* Tweak wording

* Set memory per worker to 1GiB
* why: print a dependency tree instead of enumerating paths

The old output listed every path from a direct dependency to the
package, which multiplies through diamonds in the graph (1369 lines for
Preferences in a 264-package environment). Print an inverted tree
instead: the package at the root, its dependents below, direct
dependencies highlighted, and subtrees already shown marked with (*).
The new --forward option prints the same paths top-down, starting at
the direct dependencies.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* changelog entry for why tree output

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* test: load registry before capturing why output

The fresh CI depot printed the registry install messages into the
captured IOBuffer, matching the pattern used by the other testsets.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* why: put arrows on the tree branches to show the dependency direction

The bottom-up tree uses ├─← (the child depends on the parent) and the
forward tree ├─→, matching the A → B notation the old output used.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* Reduce more package-image code duplication

- Don't specialize on `julia_version` (`VersionNumber` or `nothing`) in
  the resolve path: `resolve_versions!`, `deps_graph`, `collect_fixed!`,
  `update_manifest!`, `collect_project` and `SATResolve`.
- `atomic_toml_write`: print the TOML to a buffer and write it with a
  closure-free `atomic_write`, so the temp-dir code is compiled once
  instead of per data type, keyword set and file mode.
- `Registry.update`: convert the cooldown to `Millisecond` and pick the
  registries inside the lock closure, so the closure has one type.
- `download_source`: copy the fields it needs into a `DownloadPkg` and
  run the download code once for `PackageSpec` and `PackageEntry`.
- `@nospecialize` the `Union` arguments of `validate_manifest`,
  `status_compat_info`, `collect_project`, `GitTools.clone` and `fetch`.

Pkg package image on Julia 1.14 nightly: native code 6338 -> 5972 KiB
(-5.8%), .so 18941 -> 18015 KiB, .text 6472 -> 6103 KiB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Build REPL command tables without specializing on their entries

`PSA[...]` literals specialized `getindex` on the types of every
declaration's entries, and `CommandSpec(; dec...)`/`OptionSpec(; x...)`
compiled a keyword call per combination of keys and value types. Build
the vectors with a `@nospecialize`d `getindex` and read declarations from
a `Dict`, rejecting unknown keys as the keyword constructors did.

Pkg package image on Julia 1.14 nightly: 585 fewer code instances, .ji
-850 KiB, compressed inferred IR -166 KiB, native code -45 KiB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Honor [compat] entries for [extras] packages in resolution

A compat entry for a package listed under [extras] was only consulted by
`resolve`, which errored on it, while `update` and `instantiate` ignored
it. Add such entries as weak requirements of the loaded projects, so the
package is constrained when it is in the environment (e.g. as an
indirect dependency) without being added to it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* changelog: link PR number

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* shorten comment

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Include extras compat in the resolve hash

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Expect the `NewOnly` project to be unsatisfiable at instantiate

The `NewOnly` test project has a `[compat]` entry for its `[extras]`
package `ScientificTypes` that no version allowed by its direct
dependency satisfies. Now that extras compat is honored by
`Pkg.instantiate`, the project is already unsatisfiable before
`Pkg.test` is reached, so the test asserts that instead of expecting
`instantiate` to succeed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Drop the changelog and docs entries for extras compat

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* Fix resolving a `subdir` package that is not installed yet

When `handle_repo_add!` looks a package up by its known tree hash (e.g.
from the manifest), the object it finds is already the tree of the
package inside `subdir`. Indexing `subdir` into it again failed with
"Did not find subdirectory", so `Pkg.resolve` and `Pkg.pin` errored for
subdir packages whose manifest entry was not installed in the depot.
Skip the `subdir` step when a tree-hash lookup found a tree; rev lookups
are unchanged.

Fixes #4851

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Simplify the tree-hash check and name the repository in lookup errors

A lookup by `pkg.tree_hash` can only return the object with that hash,
which is the package tree, so the `gitobject isa LibGit2.GitTree` guard
never changes the outcome. Check `pkg.tree_hash === nothing` directly
and explain why in a comment.

The "Did not find rev" and "Did not find subdirectory" errors now name
the repository (and the rev for the subdirectory case), and the
"Did not find tree" error says that the manifest's tree is no longer
reachable and that updating the package resolves it again from its rev.
The unreachable-tree test checks that `Pkg.update` does exactly that.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Don't suggest updating a package whose recorded commit is unreachable

When the manifest's tree is no longer reachable and the package was added
at a commit, that commit is gone too, so updating fails with "Did not
find rev". Tell the user to add the package again at a rev that still
exists instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Kristoffer Carlsson <kcarlsson89@gmail.com>
Resolving with `PRESERVE_ALL_INSTALLED` (offline mode, or
`JULIA_PKG_PRESERVE_TIERED_INSTALLED`) filtered the versions of every
package in the dependency graph with `is_package_downloaded`, which probed
the depots for each version separately and re-parsed `HostPlatform()` on
every call. The graph was also expanded through the dependencies of every
version, including the ones just filtered out, so the registry data of the
whole historical dependency closure had to be parsed.

`deps_graph` now reads the installed slugs of a package once and only calls
`is_package_downloaded` for versions that are present, computes the platform
once per resolve, and only follows the dependencies of versions that passed
the filtering (the resolvers never look at the dependencies of the others).

An offline `Pkg.add("JSON")` in an empty environment on a depot with ~1500
installed packages went from ~1.1 s to ~0.14 s; `is_package_downloaded`
calls went from 42k to 1.9k and the number of packages whose registry info
is parsed from 1446 to 18. The resolved manifest is unchanged, as is the
online tier, where every non-yanked version is valid.

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Reading a compressed registry meant decompressing the tarball and walking
~75k tar entries into a Dict on every session that touched the registry
(~90 ms plus GC). Instead, build a per-registry cache file once
(registries/.cache/<Name>-<treehash>.v1.bin) with a per-package index and
the uncompressed TOML files, and mmap it so a session only pays for the
packages it uses. Loading the registry drops from ~90 ms to ~2 ms; first
Pkg.status()/Pkg.add go from ~125 ms to ~19 ms. JULIA_PKG_REGISTRY_CACHE=false
restores the old path.

On Windows a mapped file can neither be removed nor replaced, which would
break registry removal and updates, so the cache is read into memory there
instead. The tests share one cache between depots instead of building one
per depot.

Also avoid work on the first operation of a session: Registry.update no
longer asks the pkg server for registry urls when every registry is on
cooldown (an HTTPS round trip of ~100-200 ms), Resolve.logstr no longer
specializes on the type of stderr, the TOML printer used for usage logs is
precompiled explicitly, and gc's usage reduction uses a loop instead of a
broadcast.

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
An entry replaces every compatibility constraint on a package, including
those of the packages that depend on it and the project's own [compat],
with the given one, e.g. to try a new version of a package that a
dependency has not been updated to allow yet. It is keyed by a name in
[deps] or [weakdeps]; a weakdep is not added to the environment, so
that is how an indirect dependency is overridden. Only the overrides of
the loaded projects apply.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…4861)

* sandbox: ignore path `[sources]` leading out of an installed package

Packages in monorepos (OrdinaryDiffEq.jl, Lux.jl and others) ship
`[sources]` entries such as `Core = {path = "../Core"}` that point at
sibling packages in the repository. When such a package is installed by
tree hash, only its own subdirectory is on disk, so `Pkg.test` and
`Pkg.build` failed before running anything with "expected package ... to
exist at path". This made those packages impossible to test outside a
repository checkout, including under PkgEval.

When the package being tested or built is installed rather than
developed, drop the path sources of its sandbox project that lead out of
the package directory, so those dependencies resolve from the registry.
Sources inside the package, such as a `test/Project.toml` pointing at
`..`, are kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* CHANGELOG: note ignored path `[sources]` of installed packages

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* sandbox: Also ignore manifest paths leading out of an installed package

Some monorepo subpackages track their siblings through a committed
test/Manifest.toml (`path = "../../Sibling"`) instead of `[sources]`, as
BinaryReedSolomon does. Installed, those paths lead out of the package too, and
`Pkg.test` failed the same way. Leave such manifest entries out of the sandbox,
so those dependencies also resolve from the registry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The SAT resolver builds its answer layer by layer, maximizing the packages of a
layer one at a time in the `by` priority order and pinning each. That order was
the package name, so a low-level package that sorts early could be maximized
before a package depending on it and hold it back many releases. For example,
`Pkg.add("TuringGLM")` maximized `Distributions` to 0.25.131 before `Turing`, and
since the newest `Turing` (0.40.5) does not support that `Distributions`, it got
`Turing` 0.24.4, `DynamicPPL` 0.21.6 and `SciMLBase` 1.92 (39 packages older than
the maxsum resolver picks) for four patch releases of `Distributions`. PkgEval on
1.14 hit this for `TuringGLM`, `SensitivityRankCondition` and `ThreeBodyTB`, where
the old releases are broken on 1.14.

Order a layer by the size of each package's transitive dependency closure
(over the dependencies of all its versions), largest first, then by name and
UUID, so a package goes before the packages it can pull in. `closure_sizes`
takes a few milliseconds on the ~1000-package graphs of those adds.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…#4863)

#4858 replaced the keyword constructors `CommandSpec(; ...)` and
`OptionSpec(; ...)` with methods taking a declaration vector, so that Pkg
does not compile a keyword call per declaration shape. Packages that extend
the Pkg REPL with their own commands (CondaPkg, PreferenceTools) construct
`CommandSpec` with keywords, and now fail to precompile with a `MethodError`,
taking everything depending on CondaPkg/PythonCall down with them.

Add keyword methods that forward to the declaration-vector constructors.
Pkg itself does not call them, so its package image is unaffected.

Assisted-by: Claude Code (Opus 5.5)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.